#pragma once
#include <semaphore.h>
#include <vector>
#include <pthread.h>

namespace bit
{
    const int default_size = 8;
    template <class T>
    class ringQueue
    {
    private:
        // 申请信号量：这里一定是要是非const的引用！！！
        void _P(sem_t &sem)
        {
            sem_wait(&sem);
        }
        // 释放信号量：
        void _V(sem_t &sem)
        {
            sem_post(&sem);
        }

    public:
        // 构造函数：
        ringQueue(const size_t &size = default_size) : _size(size), _rqueue(size)
        {

            sem_init(&_data_sem, 0, 0);
            sem_init(&_space_sem, 0, size);

            pthread_mutex_init(&_cmutex, nullptr);
            pthread_mutex_init(&_pmutex, nullptr);
        }

        // 取数据：消费者
        void pop(T *out)
        {
            // 先申请信号量：
            // 1.申请信号量本就是原子的操作，因此不需要担心会出现多线程之间的混乱；
            // 2.先申请信号量这种方式一定程度上维护了多线程的同步关系，不至于饥饿问题的发生；
            _P(_data_sem);
            pthread_mutex_lock(&_cmutex);
            *out = _rqueue[_head];
            _head = (_head + 1) % _size;
            pthread_mutex_unlock(&_cmutex);
            _V(_space_sem);
        }

        // 存数据：生产者
        void push(const T &in)
        {
            _P(_space_sem);
            pthread_mutex_lock(&_pmutex);
            _rqueue[_end] = in;
            _end = (_end + 1) % _size;
            pthread_mutex_unlock(&_pmutex);
            _V(_data_sem);
        }

        // 析构函数：
        ~ringQueue()
        {
            sem_destroy(&_data_sem);
            sem_destroy(&_space_sem);

            pthread_mutex_destroy(&_cmutex);
            pthread_mutex_destroy(&_pmutex);
        }

    private:
        std::vector<T> _rqueue; // 环形队列；
        size_t _size;           // 环形队列的大小；

        int _head = 0; // 队列之头，消费者消费数据的位置；
        int _end = 0;  // 队列之尾：生产者生产数据的位置；

        sem_t _data_sem;  // 数据信号量，消费者关心的；
        sem_t _space_sem; // 空间信号量，生产者关心的；

        // 主要是应付多生产多消费模型：
        pthread_mutex_t _cmutex; // 消费者的互斥锁；
        pthread_mutex_t _pmutex; // 生产者的互斥锁；
    };
}